// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Text;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;

namespace svs.Mobile.BeyondMedia.FeedCore
{
	public class FeedCategory : IComparable<FeedCategory>
	{
		protected string m_Name = string.Empty;
		protected string m_Value = string.Empty;

		public FeedCategory(string name)
		{
			m_Value = m_Name = name.Trim();
		}

		public string Name
		{
			get { return m_Name; }
		}

		public string Value
		{
			get { return m_Value; }
		}

		public override bool Equals(object obj)
		{
			if (this == obj) return true;
			FeedCategory feedCategory = obj as FeedCategory;
			if (feedCategory == null) return false;
			return Equals(m_Name, feedCategory.m_Name) && Equals(m_Value, feedCategory.m_Value);
		}

		public override int GetHashCode()
		{
			return m_Name.GetHashCode() + 29*m_Value.GetHashCode();
		}

		public void RenameTo(string newName)
		{
			m_Value = m_Name = newName;
		}

		public int CompareTo(FeedCategory other)
		{
			if (other == null)
				return 1;

			return Name.CompareTo(other.Name);
		}

		public bool HasUnreadFeeds
		{
			get
			{
				FeedList categoryFeeds = FeedRepository.GetFeedsInCategory(this);
				foreach (Feed feed in categoryFeeds)
				{
					if(feed.HasUnreadItems)
						return true;
				}

				return false;
			}
		}

		public int NumberOfFeedsInCategory
		{
			get
			{
				return FeedRepository.GetFeedsInCategory(this).Count;
			}
		}

	}

	public class AllFeedsCategory : FeedCategory
	{
		public AllFeedsCategory()
			: base("All feeds")
		{
			m_Value = String.Empty;
		}
	}

	public class NullCategory : FeedCategory
	{
		public NullCategory()
			: base("<N/A>")
		{
			m_Value = String.Empty;
		}
	}

	public class CategoryNone : FeedCategory
	{
		public CategoryNone()
			: base("Uncategorized")
		{
			m_Value = "Uncategorized";
		}
	}

	public class FeedCategories
	{
		#region Data Members

		private FeedCategory m_Primary = CategoryManager.Unassigned;
		private FeedCategory m_Secondary = CategoryManager.Unassigned;
		private bool m_Modified = false;

		#endregion

		#region Public Properties

		public bool IsUnassigned
		{
			get
			{
				return Primary == CategoryManager.Unassigned && Secondary == CategoryManager.Unassigned;
			}
		}

		public FeedCategory Primary
		{
			get { return m_Primary; }
			set
			{
				m_Primary = value;
				m_Modified = true;
			}
		}

		public FeedCategory Secondary
		{
			get
			{
				return m_Secondary;
			}
			set
			{
				m_Secondary = value;
				m_Modified = true;
			}
		}

		public bool IsModified
		{
			get { return m_Modified; }
		}

		#endregion

		#region Public Methods

		public bool  IsOfCategory(FeedCategory toCheck)
		{
			if (toCheck == CategoryManager.Unassigned)
				return Primary == CategoryManager.Unassigned && Secondary == CategoryManager.Unassigned;
			else
				return Primary == toCheck || Secondary == toCheck;
		}

		public string Serialize()
		{
			return Primary.Value + "|" + Secondary.Value;
		}

		public static FeedCategories Deserialize(string serialized)
		{
			FeedCategories retVal = new FeedCategories();
			
			if(String.IsNullOrEmpty(serialized))
				return retVal;

			char[] seps = { '|' };

			string[] categories = serialized.Split(seps);

			if(categories.Length > 0)
			{
				retVal.m_Primary = LoadCategory(categories[0]);
			}

			if (categories.Length > 1)
			{
				retVal.m_Secondary = LoadCategory(categories[1]);
			}

			return retVal;
		}

		#endregion

		#region Private Methods

		private static FeedCategory LoadCategory(string catValue)
		{
			FeedCategory retVal;

			retVal = CategoryManager.GetCategoryByValue(catValue.Trim());

			if (retVal == CategoryManager.CategoryNull)
				return CategoryManager.Unassigned;
			else
				return retVal;
		}

		#endregion

	}

	public static class CategoryManager
	{
		private static readonly CategoryList m_Categories = new CategoryList();
		public static FeedCategory AllFeeds = new AllFeedsCategory();
		public static FeedCategory Unassigned = new CategoryNone();
		public static FeedCategory CategoryNull = new NullCategory();
		public const int MAX_FEED_CATEGORIES = 2;

		static CategoryManager()
		{
			Load();
		}

		#region Public Methods

		public static CategoryList GetCategoriesForFilter()
		{
			CategoryList retVal = new CategoryList();
			retVal.Add(AllFeeds);
			retVal.AddRange(m_Categories);
			retVal.Add(Unassigned);
			return retVal;
		}

		public static CategoryList GetCategoriesForEdit()
		{
			return m_Categories;
		}

		public static CategoryList GetCategoriesForAssignment()
		{
			CategoryList retVal = new CategoryList();
			retVal.Add(Unassigned);
			retVal.AddRange(m_Categories);
			return retVal;
		}

		public static CategoryList GetCategoriesForAutoUpdate()
		{
			CategoryList retVal = new CategoryList();
			retVal.Add(AllFeeds);
			retVal.AddRange(m_Categories);
			return retVal;
		}


		public static FeedCategory GetCategoryByValue(string categoryValue)
		{
			if (string.IsNullOrEmpty(categoryValue))
				return CategoryNull;

			categoryValue = categoryValue.Trim();

			if (string.Compare(Unassigned.Value, categoryValue, StringComparison.InvariantCultureIgnoreCase) == 0)
				return Unassigned;

			foreach (FeedCategory category in m_Categories)
			{
				if (string.Compare(category.Value, categoryValue, StringComparison.InvariantCultureIgnoreCase) == 0)
					return category;
			}

			return CategoryNull;
		}

		public static void DeleteCategory(FeedCategory category)
		{
			m_Categories.Remove(category);
			Save();

			if (Configuration.ActiveFeedCategory == category)
				Configuration.ActiveFeedCategory = AllFeeds;

			//Make all feeds that were in this category Unassigned
			FeedRepository.RootFeed.Feeds.ForEach(delegate(Feed f)
			                                      	{
			                                      		if (f.Categories.Primary == category)
			                                      			f.Categories.Primary = Unassigned;

			                                      		if (f.Categories.Secondary == category)
			                                      			f.Categories.Secondary = Unassigned;
			                                      	});
		}

		public static void AddCategory(string value)
		{
			AssertCategoryValueDoesNotExist(value);
			AddCategory(new FeedCategory(value));
		}

		public static void AddCategory(FeedCategory category)
		{
			if(!m_Categories.Contains(category))
			{
				m_Categories.Add(category);
				m_Categories.Sort();
				Save();
			}
		}

		public static void RenameCategory(FeedCategory category, string name)
		{
			AssertCategoryValueDoesNotExist(name);

			bool active = Configuration.ActiveFeedCategory == category;
			
			category.RenameTo(name);

			if (active)
				Configuration.ActiveFeedCategory = category;

			m_Categories.Sort();
			Save();
		}

		public static void Save()
		{
			Configuration.FeedCategories = GetSerializedCategories();
		}

		public static string GetSerializedCategories()
		{
			var sb = new StringBuilder();
			foreach (var category in m_Categories)
			{
				sb.Append(category.Value);
				sb.Append("|");
			}

			return sb.ToString().TrimEnd(new char[] { '|' });
		}

		/// <summary>
		/// True if the feed has a category that matches the active category
		/// </summary>
		/// <param name="feed"></param>
		/// <returns></returns>
		public static bool IsInActiveCategory(Feed feed)
		{
			if ((Configuration.ActiveFeedCategory == AllFeeds))
				return true;
			else
				return
					feed.Categories != null &&
					feed.Categories.IsOfCategory(Configuration.ActiveFeedCategory);
		}

		#endregion

		#region Private Methods

		private static void AssertCategoryValueDoesNotExist(string name)
		{
			if (GetCategoryByValue(name) != CategoryNull)
			{
				throw new ArgumentException("Category " + name + " already exists!");
			}
		}

		private static void Load()
		{
			Deserialize(Configuration.FeedCategories);
		}

		#endregion

		public static void Deserialize(string serialized)
		{
			char[] seps = { '|' };

			string[] categories = serialized.Split(seps);

			// get the name of the ActiveFeedCategory
			string ActiveCatName = Configuration.ActiveFeedCategory.Value;

			m_Categories.Clear();

			foreach (string s in categories)
			{
				m_Categories.Add(new FeedCategory(s));
			}

			m_Categories.Sort();

			// set the active category to the new category object
			FeedCategory fCat = GetCategoryByValue(ActiveCatName);
  		Configuration.ActiveFeedCategory = fCat!=CategoryNull ? fCat : AllFeeds; 
		}
	}
}